<template>
  <div>
    <div id="myflow_tools" v-if="!processInstanceId" class="ui-widget-content myflow_tools">
      <div id="myflow_tools_handle" style="text-align: center" class="ui-widget-header">
        工具集
      </div>
      <div class="node" id="myflow_attribute">
        <img src="@/pages/workflow/process/images/attribute.png" />属性
      </div>
      <div>
        <hr />
      </div>
      <div class="node" id="myflow_save">
        <img src="@/pages/workflow/process/images/save.gif" />保存
      </div>
      <div class="node" id="myflow_public">
        <img src="@/pages/workflow/process/images/public.png" />发布
      </div>
      <div class="node" id="myflow_export">
        <img src="@/pages/workflow/process/images/up.png" />导出
      </div>
      <div class="node" id="myflow_import">
        <img src="@/pages/workflow/process/images/down.png" />导入
      </div>
      <div>
        <hr />
      </div>
      <div class="node selectable" id="pointer">
        <img src="@/pages/workflow/process/images/select16.gif" />选择
      </div>
      <div class="node selectable" id="path">
        <img src="@/pages/workflow/process/images/16/flow_sequence.png" />转换
      </div>
      <div>
        <hr />
      </div>
      <div class="node state" id="begin" type="begin">
        <img src="@/pages/workflow/process/images/16/start_event_empty.png" />开始
      </div>
      <div class="node state" id="task" type="task">
        <img src="@/pages/workflow/process/images/16/task_empty.png" />任务
      </div>
      <div class="node state" id="subprocess" type="subprocess">
        <img src="@/pages/workflow/process/images/16/task_subprocess.png" />子流
      </div>
      <div class="node state" id="fork" type="fork">
        <img src="@/pages/workflow/process/images/16/gateway_exclusive.png" />条件
      </div>
      <div class="node state" id="join" type="join">
        <img src="@/pages/workflow/process/images/16/gateway_parallel.png" />合并
      </div>
      <div class="node state" id="end" type="end">
        <img src="@/pages/workflow/process/images/16/end_event_terminate.png" />结束
      </div>
      <div>
        <hr />
      </div>
    </div>

    <div id="eipflow"></div>

    <activitybegin ref="activityBegin" v-if="activity.begin.visible" :visible.sync="activity.begin.visible"
      :formId="activity.formId" :data="activity.currentPhaseConfig" :title="activity.begin.title" @ok="activityOk">
    </activitybegin>

    <activitytask ref="activitytask" v-if="activity.task.visible" :formId="activity.formId"
      :visible.sync="activity.task.visible" :data="activity.currentPhaseConfig" :title="activity.task.title"
      @ok="activityOk"></activitytask>

    <activitylink ref="activityLink" v-if="activity.link.visible" :visible.sync="activity.link.visible"
      :remark="activity.link.title" title="连线说明" @ok="linkOk"></activitylink>

    <activityfork ref="activityfork" v-if="activity.fork.visible" :visible.sync="activity.fork.visible"
      :data="activity.currentPhaseConfig" :title="activity.fork.title" @ok="activityOk"></activityfork>

    <activityforklink ref="activityforklink" v-if="activity.forklink.visible" :visible.sync="activity.forklink.visible"
      :data="activity.currentLine" :title="activity.forklink.title" @ok="activityForkLinkOk"></activityforklink>

    <activityend ref="activityend" v-if="activity.end.visible" :visible.sync="activity.end.visible"
      :data="activity.currentPhaseConfig" :title="activity.end.title" @ok="activityOk"></activityend>

    <activityjoin ref="activityjoin" v-if="activity.join.visible" :visible.sync="activity.join.visible"
      :data="activity.currentPhaseConfig" :title="activity.join.title" @ok="activityOk"></activityjoin>

    <activitysubprocess ref="activitysubprocess" v-if="activity.subprocess.visible"
      :visible.sync="activity.subprocess.visible" :data="activity.currentPhaseConfig" :title="activity.subprocess.title"
      :formId="activity.formId" @ok="activityOk"></activitysubprocess>

    <a-modal :zIndex="9999999" title="导入流程图Json数据" :visible="importData.visible" @cancel="importData.visible = false"
      @ok="importDataOk">
      <a-textarea v-model="importData.value" :auto-size="{ minRows: 10, maxRows: 100 }" />
    </a-modal>

    <a-modal :zIndex="9999999" title="导出流程图Json数据" :visible="exportData.visible" @cancel="exportData.visible = false"
      okText="复制" @ok="exportDataCopy()">
      <a-textarea v-model="exportData.value" :auto-size="{ minRows: 10, maxRows: 100 }" />
    </a-modal>

    <a-modal :zIndex="9999999" v-model="saveData.visible" :title="'发布流程:' + process.name" on-ok="handleOk">
      <template slot="footer">
        <a-button :loading="loading" key="back" @click="saveDataOk(false)">
          替换当前版本
        </a-button>
        <a-button :loading="loading" key="submit" type="primary" @click="saveDataOk(true)">
          发布新版本
        </a-button>
      </template>
      <div style="padding: 15px; font-size: 16px; text-align: center">
        版本:{{ process.version }}
      </div>
    </a-modal>

    <edit ref="edit" v-if="edit.visible" :visible.sync="edit.visible" :data="edit.data" :title="edit.title"
      :copy="edit.copy" :processId="edit.processId"></edit>
  </div>
</template>

<script>
import {
  save,
  findById,
  flowchart,
} from "@/services/workflow/process/desinger";
import Raphael from "raphael";
import edit from "./edit";
import activitybegin from "./activity/begin";
import activitytask from "./activity/task";
import activitylink from "./activity/link";
import activityfork from "./activity/fork";
import activityforklink from "./activity/forklink";
import activityend from "./activity/end";
import activityjoin from "./activity/join";
import activitysubprocess from "./activity/subprocess";
import { newGuid } from "@/utils/util";
export default {
  name: "sorkflowDesinger",
  components: {
    edit,
    activitybegin,
    activitylink,
    activitytask,
    activityfork,
    activityforklink,
    activityend,
    activityjoin,
    activitysubprocess,
  },
  data() {
    return {
      edit: {
        visible: false,
        processId: "",
        copy: false,
        title: "",
      },

      importData: {
        visible: false,
        value: "",
      },

      exportData: {
        visible: false,
        value: "",
      },

      saveData: {
        visible: false,
        dialog: true,
        isPublic: false,
        value: {
          activities: [],
          links: [],
          designJson: "",
          processId: this.processId,
          isNewVersion: null,
        },
      },

      loading: false,
      activity: {
        begin: {
          visible: false,
          title: "",
        },
        task: {
          visible: false,
          title: "",
        },
        link: {
          visible: false,
          title: "",
        },
        fork: {
          visible: false,
          title: "",
        },

        forklink: {
          visible: false,
          title: "",
        },

        end: {
          visible: false,
          title: "",
        },

        join: {
          visible: false,
          title: "",
        },

        subprocess: {
          visible: false,
          title: "",
        },

        currentLine: null, //选中连线
        currentPhase: null, //当前选中节点
        currentPhaseConfig: null, //当前选中节点配置

        formId: null, //默认表单Id
      },
      process: {
        version: null,
        name: null,
      },

      /**流程监控数据 */
      flowChartData: {},
    };
  },
  watch: {
    visible(val) {
      this.drawer.visible = val;
    },
  },
  props: {
    visible: {
      type: Boolean,
      default: false,
    },
    title: {
      type: String,
    },

    processId: {
      type: String,
    },

    agile: {
      type: Object,
    }, //敏捷开发实例

    //流程实例Id
    processInstanceId: {
      type: String,
    },
  },
  mounted() {
    this.initMyflow();
    this.initJpdl();
    this.initData();
  },
  methods: {
    /**
     * 取消
     */
    cancel() {
      this.$emit("update:visible", false);
    },
    /**
     * 保存
     */
    save() {
      this.saveData.isPublic = false;
      $("#myflow_save").trigger("click");
    },

    /**
     * 保存数据
     */
    saveDataOk(isNewVersion = false) {
      let that = this;
      that.$message.loading({
        content: this.saveData.isPublic ? "发布中..." : "保存中...",
      });
      this.saveData.value.isNewVersion = isNewVersion;
      this.loading = true;
      save(this.saveData.value).then(function (result) {
        that.loading = false;
        that.$message.destroy();
        if (result.code === that.eipResultCode.success) {
          that.$message.success(result.msg);
          that.saveData.visible = false;
        } else {
          that.$message.error(result.msg);
        }
      });
    },

    /**
     * 复制
     */
    exportDataCopy() {
      //创建一个input框
      const input = document.createElement("input");
      //将指定的DOM节点添加到body的末尾
      document.body.appendChild(input);
      //设置input框的value值为直播地址
      input.setAttribute("value", this.exportData.value);
      //选取文本域中的内容
      input.select();
      //copy的意思是拷贝当前选中内容到剪贴板
      //document.execCommand（）方法操纵可编辑内容区域的元素
      //返回值为一个Boolean，如果是 false 则表示操作不被支持或未被启用
      if (document.execCommand("copy")) {
        document.execCommand("copy");
      }
      //删除这个节点
      document.body.removeChild(input);
      this.$message.success("复制成功");
    },

    /**
     * 初始化数据
     */
    initData() {
      var that = this;
      if (this.processId) {
        that.$message.loading({ content: "加载中..." });
        findById(this.processId).then(function (result) {
          if (result.code === that.eipResultCode.success) {
            var designJson =
              "{states:{'" +
              newGuid() +
              "':{type:'begin',text:{text:'开始'}, attr:{ x:177, y:172,width:50, height:50}, props:{base:{'title':'开始','formName':'','isopinion':false,'isarchive':false,'titlerule':'您有一条待审批流程','titleruleTxt':'您有一条待审批流程'},timeout:[],button:[],notice:[],event:[],field:[]}},'" +
              newGuid() +
              "':{type:'end',text:{text:'结束'}, attr:{ x:517, y:174,width:50, height:50}, props:{base:{'title':'结束','formName':''},notice:[],event:[]}}},paths:{}}";
            if (result.data) {
              that.process.version = result.data.version;
              that.process.name = result.data.name;
              designJson = result.data.saveJson;
            } else {
              that.process.version = "1.0";
              that.process.name = that.agile.name;
            }
            if (that.agile) {
              that.activity.formId = that.agile.configId;
            } else {
              that.activity.formId = result.data.formId;
            }

            $(function () {
              $("#eipflow").eipflow({
                editable: true,
                restore: eval("(" + designJson + ")"),
                basePath: "",
                tools: {
                  /**
                   * 保存
                   */
                  save(data) {
                    var process = eval("(" + data + ")");
                    //转化为Json字符串
                    var activity = [],
                      link = [],
                      processId = that.processId;
                    //状态
                    $.each(process.states, function (i, item) {
                      var type = item.type;
                      activity.push({
                        activityId: i,
                        type: type,
                        title: item.text.text,
                        base: { formId: item.props.base.formId },
                        json: JSON.stringify(item),
                      });
                    });
                    //连线
                    $.each(process.paths, function (i, item) {
                      link.push({
                        linkId: i,
                        title: item.text.text,
                        begin: item.from,
                        end: item.to,
                        type: item.text.data.type,
                        judge: item.text.data.judge,
                        json: JSON.stringify(item),
                      });
                    });
                    that.saveData.value = {
                      activities: activity,
                      links: link,
                      designJson: data,
                      processId: processId,
                      isNewVersion: null,
                      isPublic: that.saveData.isPublic,
                    };
                    if (!result.data) {
                      that.saveData.value.name = that.agile.name;
                      that.saveData.value.orderNo = 1;
                      that.saveData.value.version = "1.0";
                      that.saveData.value.sn = "{年}{月}{日}{0000}";
                      that.saveData.value.showLibrary = true;
                      that.saveData.value.formId = that.agile.configId;
                      that.saveData.value.typeId = that.agile.typeId;
                      that.saveData.value.isFreeze = false;
                      that.saveData.value.icon = that.agile.icon;
                      that.saveData.value.theme = that.agile.theme;
                      that.saveData.value.iconColor = "#000";
                    }
                    that.saveDataOk(false);
                    // that.saveData.visible = true;
                  },

                  /**
                   *
                   */
                  export(data) {
                    that.exportData.visible = true;
                    that.exportData.value = data;
                  },

                  /**
                   *
                   */
                  import() {
                    that.importData.visible = true;
                  },
                },
                activity: {
                  /**
                   *
                   */
                  dblclick(currentPhase, currentPhaseConfig) {
                    var type = currentPhaseConfig.type;
                    that.activity.currentPhase = currentPhase;
                    that.activity.currentPhaseConfig = currentPhaseConfig;
                    //判断类型
                    switch (type) {
                      case "begin":
                        that.activity.begin.visible = true;
                        that.activity.begin.title =
                          currentPhaseConfig.text.text;
                        break;
                      case "task":
                        that.activity.task.visible = true;
                        that.activity.task.title = currentPhaseConfig.text.text;
                        break;
                      case "end":
                        that.activity.end.visible = true;
                        that.activity.end.title = currentPhaseConfig.text.text;
                        break;
                      case "fork":
                        that.activity.fork.visible = true;
                        that.activity.fork.title = "设置条件配置";
                        break;
                      case "join":
                        that.activity.join.visible = true;
                        that.activity.join.title = "设置合并配置";
                        break;
                      case "subprocess": //子流程
                        //that.$message.warn(that.eipMsg.buy);
                        that.activity.subprocess.visible = true;
                        that.activity.subprocess.title = "设置子流程配置";
                        break;
                      default:
                        break;
                    }
                  },

                  /**
                   *单击
                   */
                  click(currentPhase, currentPhaseConfig) { },

                  /**
                   * 悬浮
                   */
                  mouseover(currentPhase, currentPhaseConfig) {
                    console.log("悬浮");
                  },

                  /**
                   * 悬浮移除
                   */
                  mouseout(currentPhase, currentPhaseConfig) {
                    console.log("悬浮移除");
                  },
                },
                line: {
                  /**
                   * 连线双击
                   */
                  dblclick(from, to, linkObj) {
                    that.activity.currentLine = linkObj;
                    var fromConfig = from.getConfig();
                    var type = fromConfig.type,
                      text = $.trim(linkObj.attr("text"));
                    if (!type) {
                      that.$message.error("未发现点击节点类型!!!");
                      DialogTipsMsgWarn("未发现点击节点类型!!!", 2000);
                    } else {
                      //判断类型
                      switch (type) {
                        case "fork": //条件
                          that.activity.forklink.visible = true;
                          that.activity.forklink.title = text;
                          break;
                        default:
                          that.activity.link.visible = true;
                          that.activity.link.title = text;
                          break;
                      }
                    }
                  },

                  /**
                   * 连线单击
                   */
                  click(currentPhase, currentPhaseConfig) {
                    console.log("连线单击");
                  },
                },
              });
            });
            that.$message.destroy();
          } else {
            that.$message.error(result.msg);
          }
          that.spinning = false;
        });
      }

      if (this.processInstanceId) {
        that.$message.loading({ content: "加载中..." });
        flowchart({ id: this.processInstanceId }).then(function (result) {
          $(function () {
            $("#eipflow").eipflow({
              editable: false,
              restore: eval("(" + result.data.designJson + ")"),
              tools: {
                /**
                 *
                 */
                export(data) { },

                /**
                 *
                 */
                import() { },
              },
              activity: {
                /**
                 *
                 */
                dblclick(currentPhase, currentPhaseConfig) { },

                /**
                 *单击
                 */
                click(currentPhase, currentPhaseConfig) { },

                /**
                 * 悬浮
                 */
                mouseover(currentPhase, currentPhaseConfig) {
                  console.log("悬浮");
                },

                /**
                 * 悬浮移除
                 */
                mouseout(currentPhase, currentPhaseConfig) {
                  console.log("悬浮移除");
                },
              },
              line: {
                /**
                 * 连线双击
                 */
                dblclick(from, to, linkObj) { },

                /**
                 * 连线单击
                 */
                click(currentPhase, currentPhaseConfig) {
                  console.log("连线单击");
                },
              },
            });
          });
          that.$message.destroy();
        });
      }
    },

    /**
     * 导入数据
     */
    importDataOk() {
      $("#eipflow").eipflow({
        editable: true,
        restore: eval("(" + this.importData.value + ")"),
      });
      this.importData.visible = false;
    },
    /**
     * 连线保存
     */
    linkOk(content) {
      this.activity.currentLine.attr({ text: content });
    },

    /**
     *
     */
    activityForkLinkOk(data) {
      this.activity.currentLine.attr({ text: data.title });
      this.activity.currentLine.attrs.data = data;
    },

    /**
     * 活动保存
     */
    activityOk(data) {
      this.activity.currentPhase.restore(data);
    },

    /**
     * 初始化流程
     */
    initMyflow() {
      var that = this;
      var eipflow = {};
      eipflow.config = {
        editable: true,
        lineHeight: 15,
        basePath: "",
        rect: {
          // 状态
          attr: {
            x: 10,
            y: 10,
            width: 100,
            height: 50,
            r: 5,
            fill: "90-#fff-#C0C0C0",
            stroke: "#000",
            "stroke-width": 1,
          },
          showType: "image&text", // image,text,image&text
          type: "state",
          name: {
            text: "state",
            "font-style": "italic",
          },
          text: {
            text: "状态",
            "font-size": 13,
          },
          margin: 5,
          props: [],
          img: {},
        },
        path: {
          // 路径转换
          attr: {
            path: {
              path: "M10 10L100 100",
              stroke: "#808080",
              fill: "none",
              "stroke-width": 2,
            },
            //红线
            pathRed: {
              path: "M10 10L100 100",
              stroke: "red",
              fill: "none",
              "stroke-width": 4, //这个地方调整线的宽度
            },
            //绿线
            pathGreen: {
              path: "M10 10L100 100",
              stroke: "#00ff00",
              fill: "none",
              "stroke-width": 4, //这个地方调整线的宽度
            },
            arrow: {
              path: "M10 10L10 10",
              stroke: "#808080",
              fill: "#808080",
              "stroke-width": 2,
              radius: 4,
            },
            arrowRed: {
              path: "M10 10L10 10",
              stroke: "red",
              fill: "red",
              "stroke-width": 2,
              radius: 4,
            },
            arrowGreen: {
              path: "M10 10L10 10",
              stroke: "#00ff00",
              fill: "#00ff00",
              "stroke-width": 2,
              radius: 4,
            },
            fromDot: {
              width: 5,
              height: 5,
              stroke: "#fff",
              fill: "#000",
              cursor: "move",
              "stroke-width": 2,
            },
            toDot: {
              width: 5,
              height: 5,
              stroke: "#fff",
              fill: "#000",
              cursor: "move",
              "stroke-width": 2,
            },
            bigDot: {
              width: 5,
              height: 5,
              stroke: "#fff",
              fill: "#000",
              cursor: "move",
              "stroke-width": 2,
            },
            smallDot: {
              width: 5,
              height: 5,
              stroke: "#fff",
              fill: "#000",
              cursor: "move",
              "stroke-width": 3,
            },
            text: {
              cursor: "move",
              background: "#000",
              "font-size": 12,
            },
          },
          text: {
            patten: "TO {to}",
            textPos: {
              x: 0,
              y: -10,
            },
            data: {
              title: "",
              type: 0,
              formId: "",
              judge: "",
            },
          },
          props: {
            text: {
              name: "text",
              label: "显示",
              value: "",
              editor: function () { },
            },
          },
        },
        tools: {
          // 工具栏
          attr: {
            left: 10,
            top: 65,
          },
          pointer: {},
          path: {},
          states: {},
          save: function (data) {
            alert(data);
          },
          export: function (data) { },
          import: function () { },
        },
        props: {
          // 属性编辑器
          attr: {
            top: 65,
            right: 30,
          },
          props: {},
        },
        restore: "",
        activeRects: {
          // 当前激活状态
          rects: [],
          rectAttr: {
            stroke: "#ff0000",
            "stroke-width": 2,
          },
        },
        historyRects: {
          // 历史激活状态
          rects: [],
          pathAttr: {
            path: {
              stroke: "#00ff00",
            },
            arrow: {
              stroke: "#00ff00",
              fill: "#00ff00",
            },
          },
        },
        activity: {
          dblclick: function (currentPhase, currentPhaseConfig) { },
          click: function (currentPhase, currentPhaseConfig) { },
          mouseover: function (currentPhase, currentPhaseConfig) { },
          mouseout: function (currentPhase, currentPhaseConfig) { },
        },
        line: {
          dblclick: function (currentPhase, currentPhaseConfig) { },
          click: function (currentPhase, currentPhaseConfig) { },
        },
      };

      eipflow.util = {
        isLine: function (p1, p2, p3) {
          // 三个点是否在一条直线上
          var s, p2y;
          if (p1.x - p3.x == 0) s = 1;
          else s = (p1.y - p3.y) / (p1.x - p3.x);
          p2y = (p2.x - p3.x) * s + p3.y;
          // $('body').append(p2.y+'-'+p2y+'='+(p2.y-p2y)+', ');
          if (p2.y - p2y < 10 && p2.y - p2y > -10) {
            p2.y = p2y;
            return true;
          }
          return false;
        },
        center: function (p1, p2) {
          // 两个点的中间点
          return {
            x: (p1.x - p2.x) / 2 + p2.x,
            y: (p1.y - p2.y) / 2 + p2.y,
          };
        },
        nextId: (function () {
          return function () {
            var guid = "";
            for (var i = 1; i <= 32; i++) {
              var n = Math.floor(Math.random() * 16.0).toString(16);
              guid += n;
              if (i == 8 || i == 12 || i == 16 || i == 20) guid += "-";
            }
            return guid;
          };
        })(),

        connPoint: function (rect, p) {
          // 计算矩形中心到p的连线与矩形的交叉点
          var start = p,
            end = {
              x: rect.x + rect.width / 2,
              y: rect.y + rect.height / 2,
            };
          // 计算正切角度
          var tag = (end.y - start.y) / (end.x - start.x);
          tag = isNaN(tag) ? 0 : tag;

          var rectTag = rect.height / rect.width;
          // 计算箭头位置
          var xFlag = start.y < end.y ? -1 : 1,
            yFlag = start.x < end.x ? -1 : 1,
            arrowTop,
            arrowLeft;
          // 按角度判断箭头位置
          if (Math.abs(tag) > rectTag && xFlag == -1) {
            // top边
            arrowTop = end.y - rect.height / 2;
            arrowLeft = end.x + (xFlag * rect.height) / 2 / tag;
          } else if (Math.abs(tag) > rectTag && xFlag == 1) {
            // bottom边
            arrowTop = end.y + rect.height / 2;
            arrowLeft = end.x + (xFlag * rect.height) / 2 / tag;
          } else if (Math.abs(tag) < rectTag && yFlag == -1) {
            // left边
            arrowTop = end.y + ((yFlag * rect.width) / 2) * tag;
            arrowLeft = end.x - rect.width / 2;
          } else if (Math.abs(tag) < rectTag && yFlag == 1) {
            // right边
            arrowTop = end.y + (rect.width / 2) * tag;
            arrowLeft = end.x + rect.width / 2;
          }
          return {
            x: arrowLeft,
            y: arrowTop,
          };
        },

        arrow: function (p1, p2, r) {
          // 画箭头，p1 开始位置,p2 结束位置, r前头的边长
          var atan = Math.atan2(p1.y - p2.y, p2.x - p1.x) * (180 / Math.PI);

          var centerX = p2.x - r * Math.cos(atan * (Math.PI / 180));
          var centerY = p2.y + r * Math.sin(atan * (Math.PI / 180));

          var x2 = centerX + r * Math.cos((atan + 120) * (Math.PI / 180));
          var y2 = centerY - r * Math.sin((atan + 120) * (Math.PI / 180));

          var x3 = centerX + r * Math.cos((atan + 240) * (Math.PI / 180));
          var y3 = centerY - r * Math.sin((atan + 240) * (Math.PI / 180));
          return [
            p2,
            {
              x: x2,
              y: y2,
            },
            {
              x: x3,
              y: y3,
            },
          ];
        },
      };

      eipflow.rect = function (o, r, id) {
        var that = this,
          _o = $.extend(true, {}, eipflow.config.rect, o),
          _id = eipflow.util.nextId(),
          _type = "rect",
          _r = r, // Raphael画笔
          _rect,
          _img, // 图标
          _name, // 状态名称
          _text, // 显示文本
          _remarktextname, //显示备注
          _remarktexttime, //显示备注
          _remarktextinfo, //显示备注
          _ox,
          _oy, // 拖动时，保存起点位置;
          x = _o.attr.x,
          y = _o.attr.y; // 拖动时，保存起点位置;

        _o.text.text = id;
        _o.name.text = "";

        var _config = _o;

        _rect = _r
          .rect(_o.attr.x, _o.attr.y, _o.attr.width, _o.attr.height, _o.attr.r)
          .hide()
          .attr(_o.attr);

        _img = _r
          .image(
            eipflow.config.basePath + _o.img.src,
            _o.attr.x + _o.img.width / 2,
            _o.attr.y + (_o.attr.height - _o.img.height) / 2,
            _o.img.width,
            _o.img.height
          )
          .hide();
        _name = _r
          .text(
            _o.attr.x + _o.img.width + (_o.attr.width - _o.img.width) / 2,
            _o.attr.y + eipflow.config.lineHeight / 2,
            _o.name.text
          )
          .hide()
          .attr(_o.name);
        _text = _r
          .text(
            _o.attr.x + _o.img.width + (_o.attr.width - _o.img.width) / 2,
            _o.attr.y +
            (_o.attr.height - eipflow.config.lineHeight) / 2 +
            eipflow.config.lineHeight,
            _o.text.text
          )
          .hide()
          .attr(_o.text); // 文本
        _remarktextname = _r
          .text(
            x + _o.img.width + (_o.attr.width - _o.img.width) / 2,
            y +
            (_o.attr.height - eipflow.config.lineHeight) / 2 +
            eipflow.config.lineHeight,
            _o.remark
          )
          .hide()
          .attr(_o.text); // 文本
        _remarktexttime = _r
          .text(
            x + _o.img.width + (_o.attr.width - _o.img.width) / 2,
            y +
            (_o.attr.height - eipflow.config.lineHeight) / 2 +
            eipflow.config.lineHeight,
            _o.remark
          )
          .hide()
          .attr(_o.text); // 文本
        _remarktextinfo = _r
          .text(
            x + _o.img.width + (_o.attr.width - _o.img.width) / 2,
            y +
            (_o.attr.height - eipflow.config.lineHeight) / 2 +
            eipflow.config.lineHeight,
            _o.remark
          )
          .hide()
          .attr(_o.text); // 文本

        // 拖动处理----------------------------------------
        _rect.drag(
          function (dx, dy) {
            dragMove(dx, dy);
          },
          function () {
            dragStart();
          },
          function () {
            dragUp();
          }
        );
        _img.drag(
          function (dx, dy) {
            dragMove(dx, dy);
          },
          function () {
            dragStart();
          },
          function () {
            dragUp();
          }
        );
        _name.drag(
          function (dx, dy) {
            dragMove(dx, dy);
          },
          function () {
            dragStart();
          },
          function () {
            dragUp();
          }
        );
        _text.drag(
          function (dx, dy) {
            dragMove(dx, dy);
          },
          function () {
            dragStart();
          },
          function () {
            dragUp();
          }
        );

        var dragMove = function (dx, dy) {
          // 拖动中
          if (!eipflow.config.editable) return;

          var x = _ox + dx; // -((_ox+dx)%10);
          var y = _oy + dy; // -((_oy+dy)%10);

          _bbox.x = x - _o.margin;
          _bbox.y = y - _o.margin;
          resize();
        };

        var dragStart = function () {
          // 开始拖动
          _ox = _rect.attr("x");
          _oy = _rect.attr("y");
          _rect.attr({
            opacity: 0.5,
          });
          _img.attr({
            opacity: 0.5,
          });
          _text.attr({
            opacity: 0.5,
          });
        };

        var dragUp = function () {
          // 拖动结束
          _rect.attr({
            opacity: 1,
          });
          _img.attr({
            opacity: 1,
          });
          _text.attr({
            opacity: 1,
          });
        };

        // 改变大小的边框
        var _bpath,
          _bdots = {},
          _bw = 5,
          _bbox = {
            x: _o.attr.x - _o.margin,
            y: _o.attr.y - _o.margin,
            width: _o.attr.width + _o.margin * 2,
            height: _o.attr.height + _o.margin * 2,
          };

        _bpath = _r.path("M0 0L1 1").hide();
        _bdots["t"] = _r
          .rect(0, 0, _bw, _bw)
          .attr({
            fill: "#000",
            stroke: "#fff",
            cursor: "s-resize",
          })
          .hide()
          .drag(
            function (dx, dy) {
              bdragMove(dx, dy, "t");
            },
            function () {
              bdragStart(
                this.attr("x") + _bw / 2,
                this.attr("y") + _bw / 2,
                "t"
              );
            },
            function () { }
          ); // 上
        _bdots["lt"] = _r
          .rect(0, 0, _bw, _bw)
          .attr({
            fill: "#000",
            stroke: "#fff",
            cursor: "nw-resize",
          })
          .hide()
          .drag(
            function (dx, dy) {
              bdragMove(dx, dy, "lt");
            },
            function () {
              bdragStart(
                this.attr("x") + _bw / 2,
                this.attr("y") + _bw / 2,
                "lt"
              );
            },
            function () { }
          ); // 左上
        _bdots["l"] = _r
          .rect(0, 0, _bw, _bw)
          .attr({
            fill: "#000",
            stroke: "#fff",
            cursor: "w-resize",
          })
          .hide()
          .drag(
            function (dx, dy) {
              bdragMove(dx, dy, "l");
            },
            function () {
              bdragStart(
                this.attr("x") + _bw / 2,
                this.attr("y") + _bw / 2,
                "l"
              );
            },
            function () { }
          ); // 左
        _bdots["lb"] = _r
          .rect(0, 0, _bw, _bw)
          .attr({
            fill: "#000",
            stroke: "#fff",
            cursor: "sw-resize",
          })
          .hide()
          .drag(
            function (dx, dy) {
              bdragMove(dx, dy, "lb");
            },
            function () {
              bdragStart(
                this.attr("x") + _bw / 2,
                this.attr("y") + _bw / 2,
                "lb"
              );
            },
            function () { }
          ); // 左下
        _bdots["b"] = _r
          .rect(0, 0, _bw, _bw)
          .attr({
            fill: "#000",
            stroke: "#fff",
            cursor: "s-resize",
          })
          .hide()
          .drag(
            function (dx, dy) {
              bdragMove(dx, dy, "b");
            },
            function () {
              bdragStart(
                this.attr("x") + _bw / 2,
                this.attr("y") + _bw / 2,
                "b"
              );
            },
            function () { }
          ); // 下
        _bdots["rb"] = _r
          .rect(0, 0, _bw, _bw)
          .attr({
            fill: "#000",
            stroke: "#fff",
            cursor: "se-resize",
          })
          .hide()
          .drag(
            function (dx, dy) {
              bdragMove(dx, dy, "rb");
            },
            function () {
              bdragStart(
                this.attr("x") + _bw / 2,
                this.attr("y") + _bw / 2,
                "rb"
              );
            },
            function () { }
          ); // 右下
        _bdots["r"] = _r
          .rect(0, 0, _bw, _bw)
          .attr({
            fill: "#000",
            stroke: "#fff",
            cursor: "w-resize",
          })
          .hide()
          .drag(
            function (dx, dy) {
              bdragMove(dx, dy, "r");
            },
            function () {
              bdragStart(
                this.attr("x") + _bw / 2,
                this.attr("y") + _bw / 2,
                "r"
              );
            },
            function () { }
          ); // 右
        _bdots["rt"] = _r
          .rect(0, 0, _bw, _bw)
          .attr({
            fill: "#000",
            stroke: "#fff",
            cursor: "ne-resize",
          })
          .hide()
          .drag(
            function (dx, dy) {
              bdragMove(dx, dy, "rt");
            },
            function () {
              bdragStart(
                this.attr("x") + _bw / 2,
                this.attr("y") + _bw / 2,
                "rt"
              );
            },
            function () { }
          ); // 右上
        $([
          _bdots["t"].node,
          _bdots["lt"].node,
          _bdots["l"].node,
          _bdots["lb"].node,
          _bdots["b"].node,
          _bdots["rb"].node,
          _bdots["r"].node,
          _bdots["rt"].node,
        ]).click(function () {
          return false;
        });

        var bdragMove = function (dx, dy, t) {
          if (!eipflow.config.editable) return;
          var x = _bx + dx,
            y = _by + dy;
          switch (t) {
            case "t":
              _bbox.height += _bbox.y - y;
              _bbox.y = y;
              break;
            case "lt":
              _bbox.width += _bbox.x - x;
              _bbox.height += _bbox.y - y;
              _bbox.x = x;
              _bbox.y = y;
              break;
            case "l":
              _bbox.width += _bbox.x - x;
              _bbox.x = x;
              break;
            case "lb":
              _bbox.height = y - _bbox.y;
              _bbox.width += _bbox.x - x;
              _bbox.x = x;
              break;
            case "b":
              _bbox.height = y - _bbox.y;
              break;
            case "rb":
              _bbox.height = y - _bbox.y;
              _bbox.width = x - _bbox.x;
              break;
            case "r":
              _bbox.width = x - _bbox.x;
              break;
            case "rt":
              _bbox.width = x - _bbox.x;
              _bbox.height += _bbox.y - y;
              _bbox.y = y;
              break;
          }
          resize();
          // $('body').append(t);
        };
        var bdragStart = function (ox, oy, t) {
          _bx = ox;
          _by = oy;
        };

        // 事件处理--------------------------------
        //双击
        $([_rect.node, _text.node, _img.node]).bind("dblclick", function () {
          if (!eipflow.config.editable) return;
          eipflow.config.activity.dblclick(that, o);
          return false;
        });

        //悬浮事件
        $([_rect.node, _img.node]).bind("mouseover", function () {
          if (o.ispass) {
            //showActivityDoList(o.activityid);
          }
          // eipflow.config.activity.mouseover(that, o);
        });
        $([_rect.node, _img.node]).bind("mouseout", function () {
          if (o.ispass) {
            //hideActivityDoList();
          }
          // eipflow.config.activity.mouseout(that, o);
        });

        $([_rect.node, _text.node, _name.node, _img.node]).bind(
          "click",
          function () {
            if (!eipflow.config.editable) return;
            eipflow.config.activity.click(that, o);
            showBox();
            var mod = $(_r).data("mod");
            switch (mod) {
              case "pointer":
                break;
              case "path":
                var pre = $(_r).data("currNode");
                if (pre && pre.getId() != _id && pre.getType() == "rect") {
                  $(_r).trigger("addpath", [pre, that]);
                }
                break;
            }
            $(_r).trigger("click", that);
            $(_r).data("currNode", that);
            return false;
          }
        );

        var clickHandler = function (e, src) {
          if (!eipflow.config.editable) return;
          if (src.getId() == _id) {
            $(_r).trigger("showprops", [_o.props, src]);
          } else {
            hideBox();
          }
        };
        $(_r).bind("click", clickHandler);

        var textchangeHandler = function (e, text, src) {
          if (src.getId() == _id) {
            _text.attr({
              text: text,
            });
          }
        };
        $(_r).bind("textchange", textchangeHandler);

        // 私有函数-----------------------
        // 边框路径
        function getBoxPathString() {
          return (
            "M" +
            _bbox.x +
            " " +
            _bbox.y +
            "L" +
            _bbox.x +
            " " +
            (_bbox.y + _bbox.height) +
            "L" +
            (_bbox.x + _bbox.width) +
            " " +
            (_bbox.y + _bbox.height) +
            "L" +
            (_bbox.x + _bbox.width) +
            " " +
            _bbox.y +
            "L" +
            _bbox.x +
            " " +
            _bbox.y
          );
        }

        // 显示边框
        function showBox() {
          _bpath.show();
          for (var k in _bdots) {
            _bdots[k].show();
          }
        }

        // 隐藏
        function hideBox() {
          _bpath.hide();
          for (var k in _bdots) {
            _bdots[k].hide();
          }
        }

        // 根据_bbox，更新位置信息
        function resize() {
          var rx = _bbox.x + _o.margin,
            ry = _bbox.y + _o.margin,
            rw = _bbox.width - _o.margin * 2,
            rh = _bbox.height - _o.margin * 2;
          _rect.attr({
            x: rx,
            y: ry,
            width: rw,
            height: rh,
          });
          switch (_o.showType) {
            case "image":
              _img
                .attr({
                  x: rx + (rw - _o.img.width) / 2,
                  y: ry + (rh - _o.img.height) / 2,
                })
                .show();
              break;
            case "text":
              _rect.show();
              if (_o.ispass) {
                _text
                  .attr({
                    x: rx + rw / 2,
                    y: ry + rh / 2,
                  })
                  .show(); // 文本

                _remarktextname
                  .attr({
                    x: rx + rw / 2,
                    y: ry + rh / 2 + 62,
                    fill: color(_o.taskstatus),
                  })
                  .show(); // 文本
                _remarktexttime
                  .attr({
                    x: rx + rw / 2,
                    y: ry + rh / 2 + 78,
                    fill: color(_o.taskstatus),
                  })
                  .show(); // 文本
                _remarktextinfo
                  .attr({
                    x: rx + rw / 2,
                    y: ry + rh / 2 + 94,
                    fill: color(_o.taskstatus),
                  })
                  .show(); // 文本
              } else {
                _text
                  .attr({
                    x: rx + rw / 2,
                    y: ry + rh / 2,
                  })
                  .show(); // 文本
              }
              break;
            case "imagetext":
              //节点是否选中
              if (_o.ispass) {
                _text
                  .attr({
                    x: rx + _o.img.width + (rw - _o.img.width) / 2 - 22,
                    y:
                      ry +
                      (rh - eipflow.config.lineHeight) / 2 +
                      44 +
                      eipflow.config.lineHeight -
                      10,
                  })
                  .show(); // 文本
                _remarktextname
                  .attr({
                    x: rx + _o.img.width + (rw - _o.img.width) / 2 - 22,
                    y:
                      ry +
                      (rh - eipflow.config.lineHeight) / 2 +
                      44 +
                      eipflow.config.lineHeight +
                      10,
                    fill: color(_o.taskstatus),
                  })
                  .show(); // 文本
                _remarktexttime
                  .attr({
                    x: rx + _o.img.width + (rw - _o.img.width) / 2 - 22,
                    y:
                      ry +
                      (rh - eipflow.config.lineHeight) / 2 +
                      44 +
                      eipflow.config.lineHeight +
                      26,
                    fill: color(_o.taskstatus),
                  })
                  .show(); // 文本
                _remarktextinfo
                  .attr({
                    x: rx + _o.img.width + (rw - _o.img.width) / 2 - 22,
                    y:
                      ry +
                      (rh - eipflow.config.lineHeight) / 2 +
                      44 +
                      eipflow.config.lineHeight +
                      42,
                    fill: color(_o.taskstatus),
                  })
                  .show(); // 文本
              } else {
                _text
                  .attr({
                    x: rx + _o.img.width + (rw - _o.img.width) / 2 - 22,
                    y:
                      ry +
                      (rh - eipflow.config.lineHeight) / 2 +
                      44 +
                      eipflow.config.lineHeight -
                      10,
                  })
                  .show(); // 文本
              }
              _img
                .attr({
                  x: rx + (rw - _o.img.width) / 2,
                  y: ry + (rh - _o.img.height) / 2,
                })
                .show();
              break;
            case "image&text":
              _rect.show();
              _text
                .attr({
                  x: rx + _o.img.width + (rw - _o.img.width) / 2,
                  y:
                    ry +
                    (rh - eipflow.config.lineHeight) / 2 +
                    eipflow.config.lineHeight,
                })
                .show(); // 文本
              _img
                .attr({
                  x: rx + _o.img.width / 2,
                  y: ry + (rh - _o.img.height) / 2,
                })
                .show();
              break;
          }

          _bdots["t"].attr({
            x: _bbox.x + _bbox.width / 2 - _bw / 2,
            y: _bbox.y - _bw / 2,
          }); // 上
          _bdots["lt"].attr({
            x: _bbox.x - _bw / 2,
            y: _bbox.y - _bw / 2,
          }); // 左上
          _bdots["l"].attr({
            x: _bbox.x - _bw / 2,
            y: _bbox.y - _bw / 2 + _bbox.height / 2,
          }); // 左
          _bdots["lb"].attr({
            x: _bbox.x - _bw / 2,
            y: _bbox.y - _bw / 2 + _bbox.height,
          }); // 左下
          _bdots["b"].attr({
            x: _bbox.x - _bw / 2 + _bbox.width / 2,
            y: _bbox.y - _bw / 2 + _bbox.height,
          }); // 下
          _bdots["rb"].attr({
            x: _bbox.x - _bw / 2 + _bbox.width,
            y: _bbox.y - _bw / 2 + _bbox.height,
          }); // 右下
          _bdots["r"].attr({
            x: _bbox.x - _bw / 2 + _bbox.width,
            y: _bbox.y - _bw / 2 + _bbox.height / 2,
          }); // 右
          _bdots["rt"].attr({
            x: _bbox.x - _bw / 2 + _bbox.width,
            y: _bbox.y - _bw / 2,
          }); // 右上
          _bpath.attr({
            path: getBoxPathString(),
          });

          $(_r).trigger("rectresize", that);
        }

        //更换颜色
        function color(type) {
          switch (type) {
            case 4: //处理中
              return "#00a65a";
            case 6: //已完成
            case 16: //拒绝
            case 18: //已阅
              return "red";
            default:
          }
        }
        this.getConfig = function () {
          return _config;
        };
        // 函数----------------
        // 转化json字串
        this.toJson = function () {
          var data =
            "{type:'" +
            _o.type +
            "',text:{text:'" +
            $.trim(_text.attr("text")) +
            "'}, attr:{ x:" +
            Math.round(_rect.attr("x")) +
            ", y:" +
            Math.round(_rect.attr("y")) +
            ",width:" +
            Math.round(_rect.attr("width")) +
            ", height:" +
            Math.round(_rect.attr("height")) +
            "}, props:{";
          for (var k in _o.props) {
            data += k + ":" + JSON.stringify(_o.props[k]) + ",";
          }
          if (data.substring(data.length - 1, data.length) == ",")
            data = data.substring(0, data.length - 1);
          data += "}}";
          return data;
        };

        // 从数据中恢复图
        this.restore = function (data) {
          var obj = data;
          // if (typeof data === 'string')
          // obj = eval(data);

          _o = $.extend(true, _o, data);
          _o.props = data.props;

          _text.attr({
            text: obj.text.text,
          });
          if (_o.ispass) {
            var remarks = obj.remark.split("|");
            _remarktextname.attr({
              text: remarks[0],
            });
            _remarktexttime.attr({
              text: remarks[1],
            });
            _remarktextinfo.attr({
              text: remarks[2],
            });
          }
          resize();
        };

        this.getBBox = function () {
          return _bbox;
        };
        this.getId = function () {
          return _id;
        };
        this.getType = function () {
          return _type;
        };
        this.remove = function () {
          _rect.remove();
          _text.remove();
          _name.remove();
          _img.remove();
          _bpath.remove();
          for (var k in _bdots) {
            _bdots[k].remove();
          }
        };
        this.text = function () {
          return _text.attr("text");
        };
        this.attr = function (attr) {
          if (attr) _rect.attr(attr);
        };

        resize(); // 初始化位置
      };

      eipflow.path = function (o, r, from, to) {
        var that = this,
          _r = r,
          _o = $.extend(true, {}, eipflow.config.path),
          _path,
          _arrow,
          _text,
          _textPos = _o.text.textPos,
          _ox,
          _oy,
          _from = from,
          _to = to,
          _id = eipflow.util.nextId(),
          _type = "path",
          _dotList,
          _autoText = true;

        // 点
        function dot(type, pos, left, right) {
          var that = this,
            _t = type,
            _n,
            _lt = left,
            _rt = right,
            _ox,
            _oy, // 缓存移动前时位置
            _pos = pos; // 缓存位置信息{x,y}, 注意：这是计算出中心点

          switch (_t) {
            case "from":
              _n = _r
                .rect(
                  pos.x - _o.attr.fromDot.width / 2,
                  pos.y - _o.attr.fromDot.height / 2,
                  _o.attr.fromDot.width,
                  _o.attr.fromDot.height
                )
                .attr(_o.attr.fromDot);
              break;
            case "big":
              _n = _r
                .rect(
                  pos.x - _o.attr.bigDot.width / 2,
                  pos.y - _o.attr.bigDot.height / 2,
                  _o.attr.bigDot.width,
                  _o.attr.bigDot.height
                )
                .attr(_o.attr.bigDot);
              break;
            case "small":
              _n = _r
                .rect(
                  pos.x - _o.attr.smallDot.width / 2,
                  pos.y - _o.attr.smallDot.height / 2,
                  _o.attr.smallDot.width,
                  _o.attr.smallDot.height
                )
                .attr(_o.attr.smallDot);
              break;
            case "to":
              _n = _r
                .rect(
                  pos.x - _o.attr.toDot.width / 2,
                  pos.y - _o.attr.toDot.height / 2,
                  _o.attr.toDot.width,
                  _o.attr.toDot.height
                )
                .attr(_o.attr.toDot);

              break;
          }
          if (_n && (_t == "big" || _t == "small")) {
            _n.drag(
              function (dx, dy) {
                dragMove(dx, dy);
              },
              function () {
                dragStart();
              },
              function () {
                dragUp();
              }
            ); // 初始化拖动
            var dragMove = function (dx, dy) {
              // 拖动中
              var x = _ox + dx,
                y = _oy + dy;
              that.moveTo(x, y);
            };

            var dragStart = function () {
              // 开始拖动
              if (_t == "big") {
                _ox = _n.attr("x") + _o.attr.bigDot.width / 2;
                _oy = _n.attr("y") + _o.attr.bigDot.height / 2;
              }
              if (_t == "small") {
                _ox = _n.attr("x") + _o.attr.smallDot.width / 2;
                _oy = _n.attr("y") + _o.attr.smallDot.height / 2;
              }
            };

            var dragUp = function () {
              // 拖动结束
            };
          }
          $(_n.node).click(function () {
            return false;
          });

          this.type = function (t) {
            if (t) _t = t;
            else return _t;
          };
          this.node = function (n) {
            if (n) _n = n;
            else return _n;
          };
          this.left = function (l) {
            if (l) _lt = l;
            else return _lt;
          };
          this.right = function (r) {
            if (r) _rt = r;
            else return _rt;
          };
          this.remove = function () {
            _lt = null;
            _rt = null;
            _n.remove();
          };
          this.pos = function (pos) {
            if (pos) {
              _pos = pos;
              _n.attr({
                x: _pos.x - _n.attr("width") / 2,
                y: _pos.y - _n.attr("height") / 2,
              });
              return this;
            } else {
              return _pos;
            }
          };

          this.moveTo = function (x, y) {
            this.pos({
              x: x,
              y: y,
            });

            switch (_t) {
              case "from":
                if (_rt && _rt.right() && _rt.right().type() == "to") {
                  _rt.right().pos(eipflow.util.connPoint(_to.getBBox(), _pos));
                }
                if (_rt && _rt.right()) {
                  _rt.pos(eipflow.util.center(_pos, _rt.right().pos()));
                }
                break;
              case "big":
                if (_rt && _rt.right() && _rt.right().type() == "to") {
                  _rt.right().pos(eipflow.util.connPoint(_to.getBBox(), _pos));
                }
                if (_lt && _lt.left() && _lt.left().type() == "from") {
                  _lt.left().pos(eipflow.util.connPoint(_from.getBBox(), _pos));
                }
                if (_rt && _rt.right()) {
                  _rt.pos(eipflow.util.center(_pos, _rt.right().pos()));
                }
                if (_lt && _lt.left()) {
                  _lt.pos(eipflow.util.center(_pos, _lt.left().pos()));
                }
                // 三个大点在一条线上，移除中间的小点
                var pos = {
                  x: _pos.x,
                  y: _pos.y,
                };
                if (
                  eipflow.util.isLine(_lt.left().pos(), pos, _rt.right().pos())
                ) {
                  _t = "small";
                  _n.attr(_o.attr.smallDot);
                  this.pos(pos);
                  var lt = _lt;
                  _lt.left().right(_lt.right());
                  _lt = _lt.left();
                  lt.remove();
                  var rt = _rt;
                  _rt.right().left(_rt.left());
                  _rt = _rt.right();
                  rt.remove();
                  // $('body').append('ok.');
                }
                break;
              case "small": // 移动小点时，转变为大点，增加俩个小点
                if (
                  _lt &&
                  _rt &&
                  !eipflow.util.isLine(
                    _lt.pos(),
                    {
                      x: _pos.x,
                      y: _pos.y,
                    },
                    _rt.pos()
                  )
                ) {
                  _t = "big";

                  _n.attr(_o.attr.bigDot);
                  var lt = new dot(
                    "small",
                    eipflow.util.center(_lt.pos(), _pos),
                    _lt,
                    _lt.right()
                  );
                  _lt.right(lt);
                  _lt = lt;

                  var rt = new dot(
                    "small",
                    eipflow.util.center(_rt.pos(), _pos),
                    _rt.left(),
                    _rt
                  );
                  _rt.left(rt);
                  _rt = rt;
                }
                break;
              case "to":
                if (_lt && _lt.left() && _lt.left().type() == "from") {
                  _lt.left().pos(eipflow.util.connPoint(_from.getBBox(), _pos));
                }
                if (_lt && _lt.left()) {
                  _lt.pos(eipflow.util.center(_pos, _lt.left().pos()));
                }
                break;
            }

            refreshpath();
          };
        }

        function dotList() {
          // if(!_from) throw '没有from节点!';
          var _fromDot,
            _toDot,
            _fromBB = _from.getBBox(),
            _toBB = _to.getBBox(),
            _fromPos,
            _toPos;

          _fromPos = eipflow.util.connPoint(_fromBB, {
            x: _toBB.x + _toBB.width / 2,
            y: _toBB.y + _toBB.height / 2,
          });
          _toPos = eipflow.util.connPoint(_toBB, _fromPos);

          _fromDot = new dot(
            "from",
            _fromPos,
            null,
            new dot("small", {
              x: (_fromPos.x + _toPos.x) / 2,
              y: (_fromPos.y + _toPos.y) / 2,
            })
          );
          _fromDot.right().left(_fromDot);
          _toDot = new dot("to", _toPos, _fromDot.right(), null);
          _fromDot.right().right(_toDot);

          // 转换为path格式的字串
          this.toPathString = function () {
            if (!_fromDot) return "";

            var d = _fromDot,
              p = "M" + d.pos().x + " " + d.pos().y,
              arr = "";
            // 线的路径
            while (d.right()) {
              d = d.right();
              p += "L" + d.pos().x + " " + d.pos().y;
            }
            // 箭头路径
            var arrPos = eipflow.util.arrow(
              d.left().pos(),
              d.pos(),
              _o.attr.arrow.radius
            );
            arr =
              "M" +
              arrPos[0].x +
              " " +
              arrPos[0].y +
              "L" +
              arrPos[1].x +
              " " +
              arrPos[1].y +
              "L" +
              arrPos[2].x +
              " " +
              arrPos[2].y +
              "z";
            return [p, arr];
          };
          this.toJson = function () {
            var data = "[",
              d = _fromDot;

            while (d) {
              if (d.type() == "big")
                data +=
                  "{x:" +
                  Math.round(d.pos().x) +
                  ",y:" +
                  Math.round(d.pos().y) +
                  "},";
              d = d.right();
            }
            if (data.substring(data.length - 1, data.length) == ",")
              data = data.substring(0, data.length - 1);
            data += "]";
            return data;
          };
          this.restore = function (data) {
            var obj = data,
              d = _fromDot.right();

            for (var i = 0; i < obj.length; i++) {
              d.moveTo(obj[i].x, obj[i].y);
              d.moveTo(obj[i].x, obj[i].y);
              d = d.right();
            }

            this.hide();
          };

          this.fromDot = function () {
            return _fromDot;
          };
          this.toDot = function () {
            return _toDot;
          };
          this.midDot = function () {
            // 返回中间点
            var mid = _fromDot.right(),
              end = _fromDot.right().right();
            while (end.right() && end.right().right()) {
              end = end.right().right();
              mid = mid.right();
            }
            return mid;
          };
          this.show = function () {
            var d = _fromDot;
            while (d) {
              d.node().show();
              d = d.right();
            }
          };
          this.hide = function () {
            var d = _fromDot;
            while (d) {
              d.node().hide();
              d = d.right();
            }
          };
          this.remove = function () {
            var d = _fromDot;
            while (d) {
              if (d.right()) {
                d = d.right();
                d.left().remove();
              } else {
                d.remove();
                d = null;
              }
            }
          };
        }

        // 初始化操作
        _o = $.extend(true, _o, o);
        //判断连线是否通过
        if (_o.ispass) {
          _path = _r.path(_o.attr.path.path).attr(_o.attr.pathGreen);
          _arrow = _r.path(_o.attr.arrow.path).attr(_o.attr.arrowGreen);
        } else {
          _path = _r.path(_o.attr.path.path).attr(_o.attr.path);
          _arrow = _r.path(_o.attr.arrow.path).attr(_o.attr.arrow);
        }

        _dotList = new dotList();
        _dotList.hide();

        _text = _r
          .text(
            0,
            0,
            _o.text.text ||
            _o.text.patten
              .replace("{from}", _from.text())
              .replace("{to}", _to.text())
          )
          .attr(_o.attr.text);

        //添加自定义属性
        _o.text.data.title = _text.attrs.text;
        _text.attrs.data = _o.text.data;

        _text.drag(
          function (dx, dy) {
            if (!eipflow.config.editable) return;
            _text.attr({
              x: _ox + dx,
              y: _oy + dy,
            });
          },
          function () {
            _ox = _text.attr("x");
            _oy = _text.attr("y");
          },
          function () {
            var mid = _dotList.midDot().pos();
            _textPos = {
              x: _text.attr("x") - mid.x,
              y: _text.attr("y") - mid.y,
            };
          }
        );

        refreshpath(); // 初始化路径

        // 事件处理--------------------
        $([_path.node, _arrow.node, _text.node]).bind("click", function () {
          if (!eipflow.config.editable) return;
          eipflow.config.line.click(_from, _to, _text);
          $(_r).trigger("click", that);
          $(_r).data("currNode", that);
          return false;
        });
        //连线双击
        $([_path.node, _arrow.node, _text.node]).bind("dblclick", function () {
          if (!eipflow.config.editable) return;
          console.log("连线双击");
          eipflow.config.line.dblclick(_from, _to, _text);
          return false;
        });
        // 处理点击事件，线或矩形
        var clickHandler = function (e, src) {
          if (!eipflow.config.editable) return;
          if (src && src.getId() == _id) {
            _dotList.show();
            $(_r).trigger("showprops", [_o.props, that]);
          } else {
            _dotList.hide();
          }

          var mod = $(_r).data("mod");
          switch (mod) {
            case "pointer":
              break;
            case "path":
              break;
          }
        };
        $(_r).bind("click", clickHandler);

        // 删除事件处理
        var removerectHandler = function (e, src) {
          if (!eipflow.config.editable) return;
          if (
            src &&
            (src.getId() == _from.getId() || src.getId() == _to.getId())
          ) {
            // that.remove();
            $(_r).trigger("removepath", that);
          }
        };
        $(_r).bind("removerect", removerectHandler);

        // 矩形移动时间处理
        var rectresizeHandler = function (e, src) {
          if (!eipflow.config.editable) return;
          if (_from && _from.getId() == src.getId()) {
            var rp;
            if (_dotList.fromDot().right().right().type() == "to") {
              rp = {
                x: _to.getBBox().x + _to.getBBox().width / 2,
                y: _to.getBBox().y + _to.getBBox().height / 2,
              };
            } else {
              rp = _dotList.fromDot().right().right().pos();
            }
            var p = eipflow.util.connPoint(_from.getBBox(), rp);
            _dotList.fromDot().moveTo(p.x, p.y);
            refreshpath();
          }
          if (_to && _to.getId() == src.getId()) {
            var rp;
            if (_dotList.toDot().left().left().type() == "from") {
              rp = {
                x: _from.getBBox().x + _from.getBBox().width / 2,
                y: _from.getBBox().y + _from.getBBox().height / 2,
              };
            } else {
              rp = _dotList.toDot().left().left().pos();
            }
            var p = eipflow.util.connPoint(_to.getBBox(), rp);
            _dotList.toDot().moveTo(p.x, p.y);
            refreshpath();
          }
        };
        $(_r).bind("rectresize", rectresizeHandler);

        var textchangeHandler = function (e, v, src) {
          if (src.getId() == _id) {
            // 改变自身文本
            _text.attr({
              text: v,
            });
            _autoText = false;
          }
          //$('body').append('['+_autoText+','+_text.attr('text')+','+src.getId()+','+_to.getId()+']');
          if (_autoText) {
            if (_to.getId() == src.getId()) {
              //$('body').append('change!!!');
              _text.attr({
                text: _o.text.patten
                  .replace("{from}", _from.text())
                  .replace("{to}", v),
              });
            } else if (_from.getId() == src.getId()) {
              //$('body').append('change!!!');
              _text.attr({
                text: _o.text.patten
                  .replace("{from}", v)
                  .replace("{to}", _to.text()),
              });
            }
          }
        };
        $(_r).bind("textchange", textchangeHandler);

        // 函数-------------------------------------------------
        this.from = function () {
          return _from;
        };
        this.to = function () {
          return _to;
        };
        // 转化json数据
        this.toJson = function () {
          var data =
            "{from:'" +
            _from.getId() +
            "',to:'" +
            _to.getId() +
            "', dots:" +
            _dotList.toJson() +
            ",text:{text:'" +
            $.trim(_text.attr("text")) +
            "',textPos:{x:" +
            Math.round(_textPos.x) +
            ",y:" +
            Math.round(_textPos.y) +
            "}" +
            ",data:{judge:'" +
            $.trim(_text.attrs.data.judge) +
            "',formId:'" +
            $.trim(_text.attrs.data.formId) +
            "',type:" +
            $.trim(_text.attrs.data.type) +
            "}}, props:{";
          for (var k in _o.props) {
            data += k + ":" + JSON.stringify(_o.props[k]) + ",";
          }
          if (data.substring(data.length - 1, data.length) == ",")
            data = data.substring(0, data.length - 1);
          data += "}}";
          return data;
        };
        // 恢复
        this.restore = function (data) {
          var obj = data;

          _o = $.extend(true, _o, data);
          //$('body').append('['+_text.attr('text')+','+_o.text.text+']');
          if (_text.attr("text") != _o.text.text) {
            //$('body').append('['+_text.attr('text')+','+_o.text.text+']');
            _text.attr({
              text: _o.text.text,
            });
            _autoText = false;
          }

          _dotList.restore(obj.dots);
        };
        // 删除
        this.remove = function () {
          _dotList.remove();
          _path.remove();
          _arrow.remove();
          _text.remove();
          try {
            $(_r).unbind("click", clickHandler);
          } catch (e) { }
          try {
            $(_r).unbind("removerect", removerectHandler);
          } catch (e) { }
          try {
            $(_r).unbind("rectresize", rectresizeHandler);
          } catch (e) { }
          try {
            $(_r).unbind("textchange", textchangeHandler);
          } catch (e) { }
        };

        // 刷新路径
        function refreshpath() {
          var p = _dotList.toPathString(),
            mid = _dotList.midDot().pos();
          _path.attr({
            path: p[0],
          });
          _arrow.attr({
            path: p[1],
          });
          _text.attr({
            x: mid.x + _textPos.x,
            y: mid.y + _textPos.y,
          });
          // $('body').append('refresh.');
        }

        this.getId = function () {
          return _id;
        };
        this.getType = function () {
          return _type;
        };
        this.text = function () {
          return _text.attr("text");
        };
        this.attr = function (attr) {
          if (attr && attr.path) _path.attr(attr.path);
          if (attr && attr.arrow) _arrow.attr(attr.arrow);
          // $('body').append('aaaaaa');
        };
      };

      eipflow.props = function (o, r) {
        var that = this,
          _pdiv = $("#eipflow_props")
            .hide()
            .draggable({
              handle: "#eipflow_props_handle",
            })
            .resizable()
            .css(eipflow.config.props.attr)
            .bind("click", function () {
              return false;
            });
      };

      // 初始化流程
      eipflow.init = function (c, o) {
        var _w = $(window).width(),
          _h = $(window).height(),
          _r = Raphael(c, _w * 1.5, _h * 1.5),
          _states = {},
          _paths = {};

        $.extend(true, eipflow.config, o);

        /**
         * 删除： 删除状态时，触发removerect事件，连接在这个状态上当路径监听到这个事件，触发removepath删除自身；
         * 删除路径时，触发removepath事件
         */
        $(document).keydown(function (arg) {
          if (!eipflow.config.editable) return;
          if (arg.keyCode == 46) {
            var c = $(_r).data("currNode");
            if (c) {
              if (c.getType() == "rect") {
                $(_r).trigger("removerect", c);
              } else if (c.getType() == "path") {
                $(_r).trigger("removepath", c);
              }

              $(_r).removeData("currNode");
            }
          }
          // alert(arg.keyCode);
        });

        $(document).click(function () {
          $(_r).data("currNode", null);
          $(_r).trigger("click", {
            getId: function () {
              return "00000000";
            },
          });
          $(_r).trigger("showprops", [
            eipflow.config.props.props,
            {
              getId: function () {
                return "00000000";
              },
            },
          ]);
        });

        // 删除事件
        var removeHandler = function (e, src) {
          if (!eipflow.config.editable) return;
          if (src.getType() == "rect") {
            _states[src.getId()] = null;
            src.remove();
          } else if (src.getType() == "path") {
            _paths[src.getId()] = null;
            src.remove();
          }
        };
        $(_r).bind("removepath", removeHandler);
        $(_r).bind("removerect", removeHandler);

        // 添加状态
        $(_r).bind("addrect", function (e, type, o, id) {
          // $('body').append(type+', ');r
          var rect = new eipflow.rect(
            $.extend(true, {}, eipflow.config.tools.states[type], o),
            _r,
            id
          );
          _states[rect.getId()] = rect;
        });
        // 添加路径
        var addpathHandler = function (e, from, to) {
          var path = new eipflow.path({}, _r, from, to);
          _paths[path.getId()] = path;
        };
        $(_r).bind("addpath", addpathHandler);

        // 模式
        $(_r).data("mod", "point");
        if (eipflow.config.editable) {
          // 工具栏
          $("#myflow_tools")
            .draggable({
              handle: "#myflow_tools_handle",
            })
            .css(eipflow.config.tools.attr);

          $("#myflow_tools .node").hover(
            function () {
              $(this).addClass("mover");
            },
            function () {
              $(this).removeClass("mover");
            }
          );
          $("#myflow_tools .selectable").click(function () {
            $(".selected").removeClass("selected");
            $(this).addClass("selected");
            $(_r).data("mod", this.id);
          });

          $("#myflow_tools .state").each(function () {
            $(this).draggable({
              helper: "clone",
            });
          });

          $(c).droppable({
            accept: ".state",
            drop: function (event, ui) {
              //console.log(ui.helper.context);
              var temp = ui.helper.html();
              var id = temp.substring(temp.indexOf(">") + 1, temp.length);
              $(_r).trigger("addrect", [
                ui.helper.attr("type"),
                {
                  attr: {
                    x: ui.helper.offset().left,
                    y: ui.helper.offset().top - 56, //位置
                  },
                },
                id,
              ]);
              // $('body').append($(ui).attr('type')+'drop.');
            },
          });
          $("#myflow_export").click(function () {
            //导出
            var data = "{states:{";
            for (var k in _states) {
              if (_states[k]) {
                data +=
                  "'" +
                  _states[k].getId() +
                  "'" +
                  ":" +
                  _states[k].toJson() +
                  ",";
              }
            }
            if (data.substring(data.length - 1, data.length) == ",")
              data = data.substring(0, data.length - 1);
            data += "},paths:{";
            for (var k in _paths) {
              if (_paths[k]) {
                data +=
                  "'" +
                  _paths[k].getId() +
                  "'" +
                  ":" +
                  _paths[k].toJson() +
                  ",";
              }
            }
            if (data.substring(data.length - 1, data.length) == ",")
              data = data.substring(0, data.length - 1);
            data += "}}";
            eipflow.config.tools.export(data);
          });
          //导入
          $("#myflow_import").click(function () {
            eipflow.config.tools.import();
          });
          //属性
          $("#myflow_attribute").click(function () {
            that.saveData.isPublic = false;
            $("#myflow_save").trigger("click");
            that.edit.copy = false;
            that.edit.processId = that.processId;
            that.edit.title = "流程配置-" + that.process.name;
            that.edit.visible = true;
          });
          $("#myflow_save").click(function () {
            // 保存
            that.saveData.isPublic = false;
            var data = "{states:{";
            for (var k in _states) {
              if (_states[k]) {
                data +=
                  "'" +
                  _states[k].getId() +
                  "'" +
                  ":" +
                  _states[k].toJson() +
                  ",";
              }
            }
            if (data.substring(data.length - 1, data.length) == ",")
              data = data.substring(0, data.length - 1);
            data += "},paths:{";
            for (var k in _paths) {
              if (_paths[k]) {
                data +=
                  "'" +
                  _paths[k].getId() +
                  "'" +
                  ":" +
                  _paths[k].toJson() +
                  ",";
              }
            }
            if (data.substring(data.length - 1, data.length) == ",")
              data = data.substring(0, data.length - 1);
            data += "}}";

            eipflow.config.tools.save(data);
          });
          $("#myflow_public").click(function () {
            // 保存
            that.saveData.isPublic = true;
            var data = "{states:{";
            for (var k in _states) {
              if (_states[k]) {
                data +=
                  "'" +
                  _states[k].getId() +
                  "'" +
                  ":" +
                  _states[k].toJson() +
                  ",";
              }
            }
            if (data.substring(data.length - 1, data.length) == ",")
              data = data.substring(0, data.length - 1);
            data += "},paths:{";
            for (var k in _paths) {
              if (_paths[k]) {
                data +=
                  "'" +
                  _paths[k].getId() +
                  "'" +
                  ":" +
                  _paths[k].toJson() +
                  ",";
              }
            }
            if (data.substring(data.length - 1, data.length) == ",")
              data = data.substring(0, data.length - 1);
            data += "}}";

            eipflow.config.tools.save(data);
          });
          // 属性框
          new eipflow.props({}, _r);
        }
        // 恢复
        if (o.restore) {
          // var data = ((typeof o.restore === 'string') ? eval(o.restore) :
          // o.restore);
          var data = o.restore;
          var rmap = {};
          if (data.states) {
            for (var k in data.states) {
              var rect = new eipflow.rect(
                $.extend(
                  true,
                  {},
                  eipflow.config.tools.states[data.states[k].type],
                  data.states[k]
                ),
                _r
              );
              rect.restore(data.states[k]);
              rmap[k] = rect;
              _states[rect.getId()] = rect;
            }
          }
          if (data.paths) {
            for (var k in data.paths) {
              var p = new eipflow.path(
                $.extend(true, {}, eipflow.config.tools.path, data.paths[k]),
                _r,
                rmap[data.paths[k].from],
                rmap[data.paths[k].to]
              );
              p.restore(data.paths[k]);
              _paths[p.getId()] = p;
            }
          }
        }
        // 历史状态
        var hr = eipflow.config.historyRects,
          ar = eipflow.config.activeRects;
        if (hr.rects.length || ar.rects.length) {
          var pmap = {},
            rmap = {};
          for (var pid in _paths) {
            // 先组织MAP
            if (!rmap[_paths[pid].from().text()]) {
              rmap[_paths[pid].from().text()] = {
                rect: _paths[pid].from(),
                paths: {},
              };
            }
            rmap[_paths[pid].from().text()].paths[_paths[pid].text()] =
              _paths[pid];
            if (!rmap[_paths[pid].to().text()]) {
              rmap[_paths[pid].to().text()] = {
                rect: _paths[pid].to(),
                paths: {},
              };
            }
          }
          for (var i = 0; i < hr.rects.length; i++) {
            if (rmap[hr.rects[i].name]) {
              rmap[hr.rects[i].name].rect.attr(hr.rectAttr);
            }
            for (var j = 0; j < hr.rects[i].paths.length; j++) {
              if (rmap[hr.rects[i].name].paths[hr.rects[i].paths[j]]) {
                rmap[hr.rects[i].name].paths[hr.rects[i].paths[j]].attr(
                  hr.pathAttr
                );
              }
            }
          }
          for (var i = 0; i < ar.rects.length; i++) {
            if (rmap[ar.rects[i].name]) {
              rmap[ar.rects[i].name].rect.attr(ar.rectAttr);
            }
            for (var j = 0; j < ar.rects[i].paths.length; j++) {
              if (rmap[ar.rects[i].name].paths[ar.rects[i].paths[j]]) {
                rmap[ar.rects[i].name].paths[ar.rects[i].paths[j]].attr(
                  ar.pathAttr
                );
              }
            }
          }
        }
      };

      // 添加jquery方法
      $.fn.eipflow = function (o) {
        return this.each(function () {
          eipflow.init(this, o);
        });
      };

      $.eipflow = eipflow;
    },

    /**
     * 扩展流程图
     */
    initJpdl() {
      var eipflow = $.eipflow;
      $.extend(true, eipflow.config.rect, {
        attr: {
          r: 8,
          fill: "#F6F7FF",
          stroke: "#03689A",
          "stroke-width": 2,
        },
      });
      $.extend(true, eipflow.config.tools.states, {
        begin: {
          showType: "imagetext",
          type: "begin",
          name: {
            text: "<<begin>>",
          },
          text: {
            text: "开始",
          },
          img: {
            src: "/images/48/start_event_empty.png",
            width: 48,
            height: 48,
          },
          attr: {
            width: 50,
            heigth: 50,
          },
          props: {
            //基础属性
            base: {
              title: "开始",
              formId: undefined,
              formName: "",
              isopinion: false,
              isarchive: false,
              titlerule: "您有一条待审批流程",
              titleruleTxt: "您有一条待审批流程",
            },
            timeout: [], //超时
            button: [], //流程按钮
            notice: [], //通知
            event: [], //事件
            column: [], //字段
          },
        },
        end: {
          showType: "imagetext",
          type: "end",
          name: {
            text: "<<end>>",
          },
          text: {
            text: "结束",
          },
          img: {
            src: "/images/48/end_event_terminate.png",
            width: 48,
            height: 48,
          },
          attr: {
            width: 50,
            heigth: 50,
          },
          props: {
            //基础属性
            base: {
              title: "结束",
              formId: undefined,
              formName: "",
            },
            notice: [], //通知
            event: [], //事件
          },
        },

        fork: {
          showType: "imagetext",
          type: "fork",
          name: {
            text: "<<fork>>",
          },
          text: {
            text: "条件",
          },
          img: {
            src: "/images/48/gateway_exclusive.png",
            width: 48,
            height: 48,
          },
          attr: {
            width: 50,
            heigth: 50,
          },
          props: {
            base: {
              title: "条件",
            },
          },
        },

        join: {
          showType: "imagetext",
          type: "join",
          name: {
            text: "<<join>>",
          },
          text: {
            text: "合并",
          },
          img: {
            src: "/images/48/gateway_parallel.png",
            width: 48,
            height: 48,
          },
          attr: {
            width: 50,
            heigth: 50,
          },
          props: {
            base: {
              title: "合并",
              type: 0, //默认为条件
              pass: 0, //默认需要所有人通过才可以下一步
              passpercent: 0, //百分百
            },
          },
        },

        subprocess: {
          showType: "imagetext",
          type: "subprocess",
          text: { text: "子流程" },
          name: {
            text: "<<subprocess>>",
          },
          img: {
            src: "/images/48/task_subprocess.png",
            width: 48,
            height: 48,
          },
          attr: { width: 50, heigth: 50 },
          props: {
            //基础属性
            base: {
              title: "子流",
              formId: undefined,
              formName: "",
              subProcessProcessId: undefined,
              subProcessName: "",
              async: true,
              autoendstart: true,
            },
            output: [],
            input: [],
            //处理用户
            user: {
              strategy: 0, //处理策略
              chosen: 0, //选人规则
              no: 0, //无对应处理人
              auto: [], //自动同意规则
              pass: 0, //通过策略
              passConfig: 0, //通过策略配置
              approve: [], //审核处理人
              addActivity: [], //加签处理人
            },
          },
        },

        task: {
          showType: "text",
          type: "task",
          name: {
            text: "<<task>>",
          },
          text: {
            text: "任务",
          },
          img: {
            src: "/images/48/task_empty.png",
            width: 48,
            height: 48,
          },
          props: {
            //基础属性
            base: {
              title: "任务",
              formId: undefined,
              formName: "",
              isopinion: false,
              isarchive: false,
              commentstype: 0,
            },
            //处理用户
            user: {
              strategy: 0, //处理策略
              chosen: 0, //选人规则
              no: 0, //无对应处理人
              auto: [], //自动同意规则
              pass: 0, //通过策略
              passConfig: 0, //通过策略配置
              approve: [], //审核处理人
              addActivity: [], //加签处理人
            },
            timeout: [], //超时
            button: [], //流程按钮
            notice: [], //通知
            event: [], //事件
            column: [], //字段
          },
        },
      });
    },
  },
};
</script>

<style>
#eipflow {
  margin: 0;
  padding: 0;
  text-align: left;
  font-family: Arial, sans-serif, Helvetica, Tahoma;
  font-size: 12px;
  line-height: 1.5;
  color: black;
  background-image: url(images/bg.png);
}

.myflow_tools {
  position: absolute;
  top: 70px;
  left: 10;
  z-index: 999999;
  background-color: #fff;
  width: 80px;
  cursor: default;
  padding: 3px;
  font-family: Arial, sans-serif, Helvetica, Tahoma;
  font-size: 12px;
}

.node {
  width: 70px;
  text-align: center;
  vertical-align: middle;
  border: 1px solid #fff;
}

.mover {
  border: 1px solid #ddd;
  background-color: #ddd;
}

.selected {
  background-color: #ddd;
}

.state {}

#myflow_props table {}

#pointer {
  background-repeat: no-repeat;
  background-position: center;
}

#path {
  background-repeat: no-repeat;
  background-position: center;
}

#task {
  background-repeat: no-repeat;
  background-position: center;
}

#state {
  background-repeat: no-repeat;
  background-position: center;
}

.node {
  height: 25px;
  line-height: 25px;
}

.node img {
  vertical-align: middle;
  margin-right: 8px;
  margin-top: -2px;
}
</style>
